// import com.alibaba.druid.sql.ast.SQLExpr;
// import com.alibaba.druid.sql.ast.SQLExprImpl;
// import com.alibaba.druid.sql.ast.SQLObject;
// import com.alibaba.druid.sql.ast.SQLReplaceable;
// import com.alibaba.druid.sql.visitor.SQLASTVisitor;

// import java.util.ArrayList;
// import java.util.List;

#include "SQLMatchAgainstExpr.h"
#include "../../visitor/SQLASTVisitor.h"
#include "../../../utils/listAddAll.h"
#include "../../../utils/ifeq.h"
#include "../../../utils/StringUtils.h"

SQLMatchAgainstExpr_SearchModifier SQLMatchAgainstExpr_SearchModifier::IN_BOOLEAN_MODE = SQLMatchAgainstExpr_SearchModifier(1 << 0, make_string_ptr("IN BOOLEAN MODE"));
SQLMatchAgainstExpr_SearchModifier SQLMatchAgainstExpr_SearchModifier::IN_NATURAL_LANGUAGE_MODE = SQLMatchAgainstExpr_SearchModifier(1 << 1, make_string_ptr("IN NATURAL LANGUAGE MODE"));
SQLMatchAgainstExpr_SearchModifier SQLMatchAgainstExpr_SearchModifier::IN_NATURAL_LANGUAGE_MODE_WITH_QUERY_EXPANSION = SQLMatchAgainstExpr_SearchModifier(1 << 2, make_string_ptr("IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION"));
SQLMatchAgainstExpr_SearchModifier SQLMatchAgainstExpr_SearchModifier::WITH_QUERY_EXPANSION = SQLMatchAgainstExpr_SearchModifier(1 << 3, make_string_ptr("WITH QUERY EXPANSION"));

SQLMatchAgainstExpr_SearchModifier::SQLMatchAgainstExpr_SearchModifier()
    : SQLMatchAgainstExpr_SearchModifier(0, make_string_ptr(""))
{
}

SQLMatchAgainstExpr_SearchModifier::SQLMatchAgainstExpr_SearchModifier(uint64_t mask, string_ptr name)
{
  this->name = name;
  // this->nameLCase = name.toLowerCase();
  // std::transform(name->begin(), name->end(), std::back_inserter(this->nameLCase), [](unsigned char c)
  //                { return std::tolower(c); });
	this->nameLCase = StringUtils::toLower(name);
  this->mask = mask;
}

SQLMatchAgainstExpr::SQLMatchAgainstExpr()
{
  columns = SQLExpr_list_ptr();
}

SQLObject_ptr SQLMatchAgainstExpr::clone()
{
  SQLObject_ptr clone_tmp = std::shared_ptr<SQLMatchAgainstExpr>(new SQLMatchAgainstExpr());
  std::shared_ptr<SQLMatchAgainstExpr> x = std::dynamic_pointer_cast<SQLMatchAgainstExpr>(clone_tmp);
  // SQLMatchAgainstExpr x = new SQLMatchAgainstExpr();
  for (SQLExpr_ptr column : *columns)
  {
    SQLExpr_ptr column2 = std::dynamic_pointer_cast<SQLExpr>(column->clone());
    column2->setParent(x);
    x->columns->push_back(column2);
  }
  if (against != nullptr)
  {
    x->setAgainst(std::dynamic_pointer_cast<SQLExpr>(against->clone()));
  }
  x->searchModifier = searchModifier;
  return x;
}

//@Override
BOOL_ptr SQLMatchAgainstExpr::replace(SQLExpr_ptr expr, SQLExpr_ptr target)
{
  if (this->against == expr)
  {
    setAgainst(target);
    return BOOL::TRUE;
  }

  auto it_columns = columns->begin();
  for (int i = 0; i < columns->size(); i++)
  {
    std::advance(it_columns, i);
    if ((*it_columns) == expr)
    {
      target->setParent(SharedObject(SQLMatchAgainstExpr));
      (*it_columns) = target;
      return BOOL::TRUE;
    }
  }

  return BOOL::FALSE;
}

SQLExpr_list_ptr SQLMatchAgainstExpr::getColumns()
{
  return columns;
}

void SQLMatchAgainstExpr::setColumns(SQLExpr_list_ptr columns)
{
  this->columns = columns;
}

SQLExpr_ptr SQLMatchAgainstExpr::getAgainst()
{
  return against;
}

void SQLMatchAgainstExpr::setAgainst(SQLExpr_ptr against)
{
  if (against != nullptr)
  {
    against->setParent(SharedObject(SQLMatchAgainstExpr));
  }
  this->against = against;
}

std::shared_ptr<SQLMatchAgainstExpr_SearchModifier> SQLMatchAgainstExpr::getSearchModifier()
{
  return searchModifier;
}

void SQLMatchAgainstExpr::setSearchModifier(std::shared_ptr<SQLMatchAgainstExpr_SearchModifier> searchModifier)
{
  this->searchModifier = searchModifier;
}

//@Override
void SQLMatchAgainstExpr::accept0(SQLASTVisitor_ptr v)
{
  if (v->visit(SharedObject(SQLMatchAgainstExpr)))
  {
    if (columns != nullptr)
    {
      for (SQLExpr_ptr column : *this->columns)
      {
        if (column != nullptr)
        {
          column->accept(v);
        }
      }
    }

    if (this->against != nullptr)
    {
      against->accept(v);
    }
  }
  v->endVisit(SharedObject(SQLMatchAgainstExpr));
}

//@Override
SQLObject_list_ptr SQLMatchAgainstExpr::getChildren()
{
  SQLObject_list_ptr children = SQLObject_list_ptr();
  Utils::ListAddAll<SQLExpr, SQLObject>(this->columns, children);
  children->push_back(this->against);
  return children;
}

//@Override
int SQLMatchAgainstExpr::hashCode()
{
  int prime = 31;
  int result = 1;
  result = prime * result + ((against == nullptr) ? 0 : against->hashCode());
  // result = prime * result + ((columns == nullptr) ? 0 : columns->hashCode());
  result = prime * result + ((columns == nullptr) ? 0 : columns->size());
  // result = prime * result + ((searchModifier == nullptr) ? 0 : searchModifier->hashCode());
  result = prime * result + ((searchModifier == nullptr) ? 0 : searchModifier->mask);
  return result;
}

//@Override
bool SQLMatchAgainstExpr::equals(Object_ptr obj)
{
  if (this == obj.get())
  {
    return true;
  }
  if (obj == nullptr)
  {
    return false;
  }
  if (getClass() != obj->getClass())
  {
    return false;
  }
  std::shared_ptr<SQLMatchAgainstExpr> other = std::dynamic_pointer_cast<SQLMatchAgainstExpr>(obj);
  if (against == nullptr)
  {
    if (other->against != nullptr)
    {
      return false;
    }
  }
  else if (!against->equals(other->against))
  {
    return false;
  }
  if (columns == nullptr)
  {
    if (other->columns != nullptr)
    {
      return false;
    }
  }
  else if (!IFEQ::ifeq<SQLExpr_ptr>(columns, other->columns))
  {
    return false;
  }
  if (searchModifier != other->searchModifier)
  {
    return false;
  }
  return true;
}
